package com.tapadoo.alerter;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.*;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.components.element.VectorElement;
import ohos.agp.render.Canvas;
import ohos.agp.utils.Color;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.media.audio.AudioManager;
import ohos.media.common.Source;
import ohos.media.image.PixelMap;
import ohos.media.player.Player;
import ohos.utils.net.Uri;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.util.ArrayList;

/**
 * Custom Alert View
 *
 * @author Kevin Murphy, Tapadoo, Dublin, Ireland, Europe, Earth.
 * @since 26/01/2016
 **/
public class Alert extends DirectionalLayout implements Component.ClickedListener, SwipeDismissTouchListener.DismissCallbacks, Component.DrawTask {

    /**
     * The amount of time the alert will be visible on screen in seconds
     */
    private static final long DISPLAY_TIME_IN_SECONDS = 3000;
    private DirectionalLayout flClickShield;
    private DependentLayout flBackground;
    private Text tvTitle;
    private Text tvText;
    private Image ivIcon;
    private ComponentContainer rlContainer;
    private RoundProgressBar pbProgress;

    private OnHideAlertListener onHideListener;

    private long duration = DISPLAY_TIME_IN_SECONDS;

    private boolean enableIconPulse = true;
    private boolean enableInfiniteDuration;
    private boolean enableProgress;
    /**
     * Flag to ensure we only set the margins once
     */
    private boolean marginSet;

    private Component mRootComponent;

    private Component mCustomerComponent;

    private DirectionalLayout.LayoutConfig mRootLayoutConfig = new DirectionalLayout.LayoutConfig();

    private int mRootTotalHeight;

    private ComponentContainer mComponentContainerParent;

    private OnShowAlertListener onShowListener;

    private AnimatorValue animatorProgress;

    private DirectionalLayout mLLButtonContainer;

    private ArrayList<Button> buttons = new ArrayList<>();

    private boolean isEnableCustomLeftIconAnim, isEnableCustomRightIconAnim;

    private boolean isLeftBigPic = true, isRightBigPic = true, isDefaultBigPic = true;

    private boolean isEnableIconAnim;

    private int customLeftIconBig, customLeftIconSmall, customRightIconBig, customRightIconSmall;

    private static final int ANIMTION_LOOP_COUNT = 20;

    private static final int ANIMTION_LOOP_COUNT_DELAY = 500;

    private boolean isGravityPosition = false;

    private Image mRightIcon;

    private DependentLayout mDlGroup;

    private boolean isEnableRightIcon;

    private boolean isEnableOnlyRightIcon;

    private DependentLayout mDlLeftTextGroup;

    private Uri soundUri;

    public Alert(Context context, ComponentContainer componentContainer, int laytoutId) {
        this(context, null, null, componentContainer, laytoutId);
    }

    /**
     * This is the default view constructor. It requires a Context, and holds a reference to it.
     * If not cleaned up properly, memory will leak.
     *
     * @param context The Activity Context
     */
    public Alert(Context context, ComponentContainer componentContainer) {
        this(context, null, null, componentContainer, 0);
    }

    /**
     * This is the default view constructor. It requires a Context, and holds a reference to it.
     * If not cleaned up properly, memory will leak.
     *
     * @param context      The Activity Context
     * @param attrs        View Attributes
     * @param defStyleAttr Styles
     */
    public Alert(Context context, AttrSet attrs, String defStyleAttr, ComponentContainer componentContainer, int layoutId) {
        super(context, attrs, defStyleAttr);
        initView(componentContainer, layoutId);
    }

    private float componentPositionY;
    private float customComponentPositionY;

    private void initView(ComponentContainer componentContainer, int layoutId) {
        mComponentContainerParent = componentContainer;
        mRootComponent = LayoutScatter.getInstance(getContext()).parse(ResourceTable.Layout_alerter_alert_view, null, true);
        componentContainer.addComponent(mRootComponent);
        if (layoutId != 0) {
            mCustomerComponent = LayoutScatter.getInstance(getContext()).parse(layoutId, null, true);
            componentContainer.addComponent(mCustomerComponent);
        }
        LogUtil.error("Alert", "1==========initview==========");
        AnimatorValue animator = new AnimatorValue();
        animator.setDuration(100);
        animator.setDelay(20);
        animator.setCurveType(Animator.CurveType.ACCELERATE);
        mRootTotalHeight = mRootComponent.getHeight();
        componentPositionY = mRootComponent.getContentPositionY();
        if (mCustomerComponent != null) {
            customComponentPositionY = mCustomerComponent.getContentPositionY();
        }
        animator.setValueUpdateListener((animatorValue, v) -> {
            LogUtil.error("Alert", "1======v===" + v + "," + mRootComponent.getContentPositionY());
            showButtons();
            showProgress();
            mRootComponent.setContentPosition(mRootComponent.getContentPositionX(), mRootComponent.getContentPositionY() + (int) (50 * v));
            mRootComponent.invalidate();
            if (mCustomerComponent != null) {
                mCustomerComponent.setContentPosition(mCustomerComponent.getContentPositionX(), mCustomerComponent.getContentPositionY() + (int) (200 * v));
                mCustomerComponent.invalidate();
            }
            if (v == 1) {
                LogUtil.error("Alert", "1======v===finish====" + v);

                if (onShowListener != null) {
                    onShowListener.onShow();
                }
                if (!isGravityPosition) {
                    mRootComponent.setContentPosition(mRootComponent.getContentPositionX(), componentPositionY);
                    mRootComponent.invalidate();
                    if (mCustomerComponent != null) {
                        mCustomerComponent.setContentPosition(mCustomerComponent.getContentPositionX(), customComponentPositionY);
                        mCustomerComponent.invalidate();
                    }
                }
                EventHandler eventHandler = new EventHandler(EventRunner.create());
                eventHandler.postTask(() -> getContext().getUITaskDispatcher().asyncDispatch(() -> {
                    AnimatorValue animatorHide = new AnimatorValue();
                    animatorHide.setDuration(100);
                    animatorHide.setDelay(20);
                    animatorHide.setCurveType(Animator.CurveType.ACCELERATE);
                    animatorHide.setValueUpdateListener((animatorValue1, v1) -> {
                        if (isGravityPosition) {
                            componentContainer.removeComponent(mRootComponent);
                            if (mCustomerComponent != null) {
                                componentContainer.removeComponent(mCustomerComponent);
                            }
                            if (onHideListener != null) {
                                onHideListener.onHide();
                            }
                            if (animatorProgress != null) {
                                animatorProgress.cancel();
                            }
                        } else {
                            mRootLayoutConfig.height = mRootTotalHeight - (int) (100 * v1);
                            mRootLayoutConfig.width = LayoutConfig.MATCH_PARENT;
                            mRootComponent.setLayoutConfig(mRootLayoutConfig);
                            mRootComponent.invalidate();
                            if (mCustomerComponent != null) {
                                mCustomerComponent.setLayoutConfig(mRootLayoutConfig);
                                mCustomerComponent.invalidate();
                            }
                            if (v1 == 1) {
                                componentContainer.removeComponent(mRootComponent);
                                if (mCustomerComponent != null) {
                                    componentContainer.removeComponent(mCustomerComponent);
                                }
                                if (onHideListener != null) {
                                    onHideListener.onHide();
                                }
                                if (animatorProgress != null) {
                                    animatorProgress.cancel();
                                }
                            }
                        }
                    });
                    animatorHide.start();
                }), duration);
            }
        });
        animator.start();

        flBackground = (DependentLayout) mRootComponent.findComponentById(ResourceTable.Id_flAlertBackground);
        flClickShield = (DirectionalLayout) mRootComponent.findComponentById(ResourceTable.Id_flClickShield);
        ivIcon = (Image) mRootComponent.findComponentById(ResourceTable.Id_ivIcon);
        tvTitle = (Text) mRootComponent.findComponentById(ResourceTable.Id_tvTitle);
        tvText = (Text) mRootComponent.findComponentById(ResourceTable.Id_tvText);
        rlContainer = (ComponentContainer) mRootComponent.findComponentById(ResourceTable.Id_rlContainer);
        pbProgress = (RoundProgressBar) mRootComponent.findComponentById(ResourceTable.Id_pbProgress);
        mLLButtonContainer = (DirectionalLayout) mRootComponent.findComponentById(ResourceTable.Id_llButtonContainer);
        mRightIcon = (Image) mRootComponent.findComponentById(ResourceTable.Id_ivIconRight);
        mDlGroup = (DependentLayout) mRootComponent.findComponentById(ResourceTable.Id_dlRight);
        mDlLeftTextGroup = (DependentLayout) mRootComponent.findComponentById(ResourceTable.Id_dlLeft);
        flBackground.setClickedListener(this);
        addDrawTask(this);
    }

    public void setEnableCustomLeftIconAnim(boolean enableCustomLeftIconAnim) {
        isEnableCustomLeftIconAnim = enableCustomLeftIconAnim;
    }

    public void setEnableCustomRightIconAnim(boolean enableCustomRightIconAnim) {
        isEnableCustomRightIconAnim = enableCustomRightIconAnim;
    }

    public void setEnableIconAnim(boolean enableIconAnim) {
        isEnableIconAnim = enableIconAnim;
        handleShowLeftIconAnim();
    }

    public void setEnableRightIcon(boolean enableRightIcon) {
        isEnableRightIcon = enableRightIcon;
    }

    public void setEnableOnlyRightIcon(boolean enableOnlyRightIcon) {
        isEnableOnlyRightIcon = enableOnlyRightIcon;
    }

    private void handleShowLeftIconAnim() {
        //校验右边图标是否开启
        if (isEnableRightIcon) {
            mDlGroup.setVisibility(Component.VISIBLE);
            if (isEnableOnlyRightIcon) {
                ivIcon.setVisibility(Component.HIDE);
            }
        }
        if (!isEnableIconAnim) return;
        LogUtil.error(this.getName(), "1=========isEnableCustomLeftIconAnim:"
                + isEnableCustomLeftIconAnim + ",isEnableCustomRightIconAnim:"
                + isEnableCustomRightIconAnim);
        //自定义左图标动画
        if (isEnableCustomLeftIconAnim) {
            if (customLeftIconBig == 0 || customLeftIconSmall == 0) return;
            new Thread(() -> {
                try {
                    for (int i = 0; i < ANIMTION_LOOP_COUNT; i++) {
                        getContext().getUITaskDispatcher().asyncDispatch(() -> {
                            if (isLeftBigPic) {
                                isLeftBigPic = false;
                                ivIcon.setImageAndDecodeBounds(customLeftIconSmall);
                            } else {
                                isLeftBigPic = true;
                                ivIcon.setImageAndDecodeBounds(customLeftIconBig);
                            }
                            ivIcon.invalidate();
                        });
                        Thread.sleep(ANIMTION_LOOP_COUNT_DELAY);
                    }
                } catch (Exception e) {
                    LogUtil.error(this.getName(), "handleShowLeftIconAnim--error:" + e);
                }
            }).start();
        }

        //自定义右图标动画
        if (isEnableCustomRightIconAnim) {
            if (customRightIconBig == 0 || customRightIconSmall == 0) return;
            new Thread(() -> {
                try {
                    for (int i = 0; i < ANIMTION_LOOP_COUNT; i++) {
                        getContext().getUITaskDispatcher().asyncDispatch(() -> {
                            if (isRightBigPic) {
                                mRightIcon.setImageAndDecodeBounds(customRightIconSmall);
                                isRightBigPic = false;
                            } else {
                                mRightIcon.setImageAndDecodeBounds(customRightIconBig);
                                isRightBigPic = true;
                            }
                            mRightIcon.invalidate();
                        });
                        Thread.sleep(ANIMTION_LOOP_COUNT_DELAY);
                    }
                } catch (Exception e) {
                    LogUtil.error(this.getName(), "handleShowLeftIconAnim--error:" + e);
                }
            }).start();
        }

        //左右固定图标动画
        new Thread(() -> {
            try {
                for (int i = 0; i < ANIMTION_LOOP_COUNT; i++) {
                    getContext().getUITaskDispatcher().asyncDispatch(() -> {
                        VectorElement vectorElement = null;
                        if (isDefaultBigPic) {
                            vectorElement = new VectorElement(getContext(), ResourceTable.Graphic_alerter_ic_notifications_small);
                            isDefaultBigPic = false;
                        } else {
                            vectorElement = new VectorElement(getContext(), ResourceTable.Graphic_alerter_ic_notifications);
                            isDefaultBigPic = true;
                        }
                        if (!isEnableCustomRightIconAnim) {
                            LogUtil.error(this.getName(), "1=========!isEnableCustomRightIconAnim");
                            mRightIcon.setImageElement(vectorElement);
                            mRightIcon.invalidate();
                        }
                        if (!isEnableCustomLeftIconAnim) {
                            LogUtil.error(this.getName(), "1=========!isEnableCustomLeftIconAnim");
                            ivIcon.setImageElement(vectorElement);
                            ivIcon.invalidate();
                        }
                    });
                    Thread.sleep(ANIMTION_LOOP_COUNT_DELAY);
                }
            } catch (Exception e) {
                LogUtil.error(this.getName(), "handleShowLeftIconAnim--error:" + e);
            }
        }).start();

        LogUtil.error(this.getName(), "1========soundUri===" + soundUri);

        if (soundUri != null) {
            try {
                File mp3File = new File(soundUri.toString());
                FileInputStream fileInputStream = new FileInputStream(mp3File);
                FileDescriptor fileDescriptor = fileInputStream.getFD();
                Player player = new Player(getContext());
                player.setSource(new Source(fileDescriptor));
                player.prepare();
                player.play();
            } catch (Exception e) {
                LogUtil.error(this.getName(), "1========soundUri===error==" + e.toString());
            }
        }
    }


    private void showButtons() {
        if (buttons == null) return;
        for (int i = 0; i < buttons.size(); i++) {
            mLLButtonContainer.addComponent(buttons.get(i));
        }
    }


    private void showProgress() {
        if (!enableProgress) return;
        pbProgress.setVisibility(Component.VISIBLE);
        ivIcon.setVisibility(Component.INVISIBLE);
        animatorProgress = new AnimatorValue();
        animatorProgress.setDuration(1000);
        animatorProgress.setDelay(100);
        animatorProgress.setLoopedCount(20);
        animatorProgress.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE);
        animatorProgress.setValueUpdateListener((animatorValue, v) -> {
            LogUtil.error("Alert", "1========showProgress=====" + (v) * 10);
            getProgressBar().setProgressValue((int) (v * 10));
        });
        animatorProgress.start();
    }

    public void setOnClickListener(final Component.ClickedListener listener) {
        flBackground.setClickedListener(listener);
    }

    @Override
    public void setVisibility(final int visibility) {
        super.setVisibility(visibility);
        for (int i = 0; i < getChildCount(); i++) {
            getComponentAt(i).setVisibility(visibility);
        }
    }


    /**
     * Cleans up the currently showing alert view.
     */
    public void hide() {
        AnimatorValue animatorHide = new AnimatorValue();
        animatorHide.setDuration(50);
        animatorHide.setDelay(20);
        animatorHide.setCurveType(Animator.CurveType.ACCELERATE);
        animatorHide.setValueUpdateListener((animatorValue, v) -> {
            mRootLayoutConfig.height = mRootTotalHeight - (int) (100 * v);
            mRootLayoutConfig.width = LayoutConfig.MATCH_PARENT;
            mRootComponent.setLayoutConfig(mRootLayoutConfig);
            mRootComponent.invalidate();
            if (v == 1) {
                mComponentContainerParent.removeComponent(mRootComponent);
                if (onHideListener != null) {
                    onHideListener.onHide();
                }
            }
        });
        animatorHide.start();
    }

    public void addButton(String text, int textColor, int backgroundColor, int width, int height, Component.ClickedListener clickedListener) {
        Button btnButton = new Button(getContext());
        btnButton.setText(text);
        btnButton.setTextColor(new Color(textColor));
        btnButton.setWidth(width);
        btnButton.setHeight(height);
        btnButton.setClickedListener(clickedListener);
        ComponentContainer.LayoutConfig btnLayoutConfig = new ComponentContainer.LayoutConfig();
        btnLayoutConfig.width = width;
        btnLayoutConfig.height = height;
        btnLayoutConfig.setMarginLeft(20);
        btnButton.setLayoutConfig(btnLayoutConfig);
        ShapeElement shapeElement = new ShapeElement();
        shapeElement.setRgbColor(TextUtils.convertColor(backgroundColor));
        btnButton.setBackground(shapeElement);
        mLLButtonContainer.addComponent(btnButton);
    }


    /**
     * Sets the Alert Background colour
     *
     * @param color The qualified colour integer
     */
    public void setAlertBackgroundColor(final int color) {
        ShapeElement shapeElement = new ShapeElement();
        shapeElement.setRgbColor(TextUtils.convertColor(color));
        flBackground.setBackground(shapeElement);
    }


    public int getContentGravity() {
//        return ((LayoutParams) rlContainer.getLayoutParams()).gravity;
        return 0;
    }

    /**
     * Sets the Gravity of the Alert
     *
     * @param contentGravity Gravity of the Alert
     */
    public void setContentGravity(final int contentGravity) {
        //TODO 主要用于居中、底部弹窗
        isGravityPosition = true;
        DependentLayout.LayoutConfig layoutConfig = new DependentLayout.LayoutConfig();
        layoutConfig.height = 500;
        layoutConfig.addRule(contentGravity);
        mRootComponent.setLayoutConfig(layoutConfig);
    }

    /**
     * Disable touches while the Alert is showing
     */
    public void disableOutsideTouch() {
        flClickShield.setClickable(true);
    }

    public DependentLayout getAlertBackground() {
        return flBackground;
    }

    public Text getTitle() {
        return tvTitle;
    }

    /**
     * Sets the Title of the Alert
     *
     * @param title String object to be used as the Alert title
     */
    public void setTitle(final String title) {
        if (!TextUtils.isEmpty(title)) {
            tvTitle.setVisibility(VISIBLE);
            tvTitle.setText(title);
        }
    }


    public Text getText() {
        return tvText;
    }

    /**
     * Sets the Text of the Alert
     *
     * @param text String resource id of the Alert text
     */
    public void setText(final String text) {
        if (!TextUtils.isEmpty(text)) {
            tvText.setVisibility(VISIBLE);
            tvText.setText(text);
            int len = tvText.getText().length();
            int difflen = len - 109;
            int line = difflen / 27;
            int height = 160 + 40 * line;
            if (len > 80) {
                flClickShield.setHeight(ScaleUtils.vp2px(getContext(), height));
            }
        }
    }


    public Image getIcon() {
        return ivIcon;
    }

    /**
     * Set the inline icon for the Alert
     *
     * @param iconId Drawable resource id of the icon to use in the Alert
     */
    public void setIcon(final int iconId) {
        ivIcon.setImageAndDecodeBounds(iconId);
    }


    /**
     * Set the inline icon for the Alert
     *
     * @param bitmap Bitmap image of the icon to use in the Alert.
     */
    public void setIcon(final PixelMap bitmap) {
        ivIcon.setPixelMap(bitmap);
    }

    /**
     * Set the inline icon for the Alert
     *
     * @param drawable Drawable image of the icon to use in the Alert.
     */
    public void setIcon(final Element drawable) {
        ivIcon.setImageElement(drawable);
    }

    public void setIconSize(final int size) {
        ivIcon.setWidth(size);
        ivIcon.setHeight(size);
    }

    /**
     * Set whether to show the icon in the alert or not
     *
     * @param showIcon True to show the icon, false otherwise
     */
    public void showIcon(final boolean showIcon) {
        ivIcon.setVisibility(showIcon ? Component.VISIBLE : Component.HIDE);
    }

    /**
     * Set whether to enable swipe to dismiss or not
     */
    public void enableSwipeToDismiss() {
        flBackground.setTouchEventListener(new SwipeDismissTouchListener(flBackground, null, this));
    }

    /**
     * Get the Alert's on screen duration
     *
     * @return The given duration, defaulting to 3000 milliseconds
     */
    public long getDuration() {
        return duration;
    }

    /**
     * Set the alert's on screen duation
     *
     * @param duration The duration of alert on screen
     */
    public void setDuration(final long duration) {
        this.duration = duration;
    }

    /**
     * Set if the Icon should pulse or not
     *
     * @param shouldPulse True if the icon should be animated
     */
    public void pulseIcon(final boolean shouldPulse) {
        this.enableIconPulse = shouldPulse;
    }

    /**
     * Set if the duration of the alert is infinite
     *
     * @param enableInfiniteDuration True if the duration of the alert is infinite
     */
    public void setEnableInfiniteDuration(final boolean enableInfiniteDuration) {
        this.enableInfiniteDuration = enableInfiniteDuration;
    }

    /**
     * Enable or disable progress bar
     *
     * @param enableProgress True to enable, False to disable
     */
    public void setEnableProgress(final boolean enableProgress) {
        this.enableProgress = enableProgress;
    }

    /**
     * Set the Progress bar color from a color resource
     *
     * @param color The color resource
     */
    public void setProgressColorInt(final int color) {
        pbProgress.setProgressColor(new Color(color));
    }


    /**
     * Set the alert's listener to be fired on the alert being fully hidden
     *
     * @param listener Listener to be fired
     */
    public void setOnHideListener(final OnHideAlertListener listener) {
        this.onHideListener = listener;
    }

    public void setOnShowListener(final OnShowAlertListener listener) {
        this.onShowListener = listener;
    }

    public void setCustomLeftIconBig(int customLeftIconBig) {
        this.customLeftIconBig = customLeftIconBig;
    }

    public void setCustomLeftIconSmall(int customLeftIconSmall) {
        this.customLeftIconSmall = customLeftIconSmall;
    }

    public void setCustomRightIconBig(int customRightIconBig) {
        this.customRightIconBig = customRightIconBig;
    }

    public void setCustomRightIconSmall(int customRightIconSmall) {
        this.customRightIconSmall = customRightIconSmall;
    }

    public void setRightIconTop() {
        mDlLeftTextGroup.setVisibility(HIDE);
        DependentLayout.LayoutConfig layoutConfig = new DependentLayout.LayoutConfig();
        layoutConfig.height = 100;
        layoutConfig.width = 100;
        layoutConfig.setMarginTop(20);
        layoutConfig.addRule(DependentLayout.LayoutConfig.ALIGN_PARENT_TOP);
        mRightIcon.setLayoutConfig(layoutConfig);
        layoutConfig = new DependentLayout.LayoutConfig();
        layoutConfig.height = DependentLayout.LayoutConfig.MATCH_CONTENT;
        layoutConfig.width = DependentLayout.LayoutConfig.MATCH_CONTENT;
        layoutConfig.setMarginLeft(10);
        layoutConfig.setMarginRight(100);
        layoutConfig.addRule(DependentLayout.LayoutConfig.ALIGN_PARENT_LEFT);
        tvText.setLayoutConfig(layoutConfig);
        tvTitle.setLayoutConfig(layoutConfig);
    }

    public void setSound(Uri uri) {
        soundUri = uri;
    }

    @Override
    public boolean canDismiss(final Object token) {
        return true;
    }

    @Override
    public void onDismiss(Component view, Object token) {
//        flClickShield.removeView(flBackground);
    }

    @Override
    public void onTouch(Component view, boolean touch) {
//        if (touch) {
//            removeCallbacks(runningAnimation);
//        } else {
//            startHideAnimation();
//        }
        hide();
    }
//
//    @Override
//    public void onTouch(final View view, final boolean touch) {
//        if (touch) {
//            removeCallbacks(runningAnimation);
//        } else {
//            startHideAnimation();
//        }
//    }

    /**
     * Get the progress bar
     *
     * @return Progress bar
     */
    public RoundProgressBar getProgressBar() {
        return pbProgress;
    }

    /**
     * Get the on hide listener
     *
     * @return On hide listener
     */
    public OnHideAlertListener getOnHideListener() {
        return onHideListener;
    }


    @Override
    public void onClick(Component component) {
        hide();
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        if (!marginSet) {
            marginSet = true;

            final ComponentContainer.LayoutConfig params = (ComponentContainer.LayoutConfig) getLayoutConfig();
            params.setMarginTop(-26);
            invalidate();
        }
    }
}